###############################################################################

"""
 understand.py
    this module is offically up and running. though changes may be made the
    return type and format is going to stay the same.
    input is raw text entered by the user. this is first processed to see if
    the user already knew the fact presented. then the input is passed on to
    get the noun phrases extracted.

    understand.meaning() returns a list of lists (final_noun_phrases). the
    first list contains one string with one of the following values to
    indicate if the user already knew the fact presented: "yes", "no", or
    "unknown". "unknown" only occurs when there is a one word responce that
    can't be matched with the hard coded words.
    
    note 1: the package "numpy" needs to be installed for this to work
    it is used by nltk for part of speech tagging and can be found at
    http://numpy.scipy.org/
"""

import nltk

"""
tag_input()
    break input text into tokens and then give them part of speech tags
"""
def tag_input (input_text):
    pos_tag_set = []

    # tokenize text and then assign pos tags to each token
    text = nltk.word_tokenize(input_text)
    pos_tag_set = nltk.pos_tag(text)
    
    return pos_tag_set


"""
trim_noun_phrase()
    takes in a noun phrase and turns it into a list of strings
"""
def trim_noun_phrase (full_noun_phrase):

    final_noun_phrase = []
    
    # check if word is a DT or PP type and removes it if so
    if (full_noun_phrase[0][1][0] == 'D' or
        full_noun_phrase[0][1][0] == 'P'):
        full_noun_phrase.remove(full_noun_phrase[0])
    
    # makes a list of just the strings
    for (x, y) in full_noun_phrase:
        final_noun_phrase.append(x)        
    return final_noun_phrase


"""
get_noun_phrase()
    extracts and returns noun phrases
"""
def get_noun_phrases (pos_tag_set):

    full_noun_phrase = []
    final_noun_phrases = []
    
    #the pseudo regexp's for noun phrases (NPs).
    #  <DT|PP\$>?: an optional determiner (a,the) or posesive (his, hers).
    #    note: '$' is literal so needs to be backslashed
    #  <JJ.*>* zero or more adjuctives of any type
    #  <NN.*>+ one or more nouns of any type
    #  <CD> a number of sorts
    grammar = "NP: {(<DT|PP\$>?<JJ.*>*<NN.*>+)|<CD>}"

    #look for noun phrases and then parse sentence into tree
    chunk_parser = nltk.RegexpParser(grammar)
    chunk_grammar_tree = chunk_parser.parse(pos_tag_set)
    
    #search the tree for noun phrases.
    #when noun phrase is found trim it and append it to final_noun_phrases
    for subtree in chunk_grammar_tree.subtrees(filter =
                                               lambda t: t.node == 'NP'):
        full_noun_phrase = list(subtree.leaves())
        final_noun_phrases.append(trim_noun_phrase(full_noun_phrase))
    
    return final_noun_phrases

"""
check_was_fact_known()
    first cheks for one word answers likely to be typed as a responce
    it is assumed the answer is positive (the user knew the fact) so
    only negations are c hecked for.
    this wil make lots of false positives (should really do something about
    this!!!) if the user enters something not pertanent.
"""
def check_was_fact_known (pos_tag_set):
    #assumed true unless negation is found
    was_fact_known = ["yes"]
    #lambda functions for finding a yes, no, negation, or quit instance
    yes = lambda x: (x == "y" or x == "yes" or x == "yea" or x == "yep" or
                     x == "yup" or x == "yeah" or x == "aye" or x == "amen" or
                     x == "duh" or x == "arr" or x == "yeah" )
    no = lambda x: (x == "n" or x == "no" or x == "nope" or x == "na" or
                    x == "nah" or x == "nay" or x == "nix" or x == "negative")
    negation = lambda x: (x == "not" or x == "n't")
    quit = lambda x: (x == "q" or x == "x" or x == "quit" or x == "exit")

    #check first word for yes no or quit instance
    if yes(pos_tag_set[0][0]):
        was_fact_known = ["yes"]
    elif no(pos_tag_set[0][0]):
        was_fact_known = ["no"]
    elif quit(pos_tag_set[0][0]):
        was_fact_known = ["quit"]
    elif len(pos_tag_set) == 1:
        was_fact_known = [["error"], ["unknown one word answer"]]

    #check for negation
    for (x, y) in pos_tag_set:
        if no(x) or negation(x):
            was_fact_known = ["no"]
            return was_fact_known
    
    return was_fact_known


def get_meaning (input_text):
    final_noun_phrases = []
    was_fact_known = []

    if len(input_text) == 0:
        final_noun_phrases.extend([["error"], ["empty string"]])
        return final_noun_phrases

    pos_tag_set = tag_input(input_text)
    #print nltk.ne_chunk(pos_tag_set)

    final_noun_phrases.extend(check_was_fact_known(pos_tag_set))
    final_noun_phrases.extend(get_noun_phrases(pos_tag_set))
    return final_noun_phrases

